package cloud.xlh.my_system.aspect;

import cloud.xlh.exceptionhandle.service.impl.CommonExceptionEnum;
import cloud.xlh.infrastructure.cache.operate.CacheOperateService;
import cloud.xlh.my_system.annotation.ServiceConfig;
import cloud.xlh.my_system.in.ServiceIn;
import cloud.xlh.my_system.pojo.JsonContext;
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Optional;

/**
 * Created with IntelliJ IDEA.
 *
 * @ project name : xlh_osms
 * @ Author: XuLeHuang
 * @ Date: 2022/11/16 22:32
 * @ Description: 服务统一入口切面类
 */
@Component
@Aspect
public class ServiceCallAspect {
    @Autowired
    private ServiceIn serviceIn;

    @Resource(name = "Caffeine")
    private CacheOperateService caffeineCacheOperateService;

    @Pointcut("@annotation(cloud.xlh.my_system.annotation.ServiceConfig)")
    public void serviceConfigPointCut() {

    }

    @Before(value = "serviceConfigPointCut() && @annotation(serviceConfig)")
    public void ServiceCall(JoinPoint joinPoint, ServiceConfig serviceConfig) throws InstantiationException {
        //@ServiceConfig标注的方法
        /*MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();*/

        String originalPath = serviceConfig.originalPath().replace("/", "");

        Class<?> value = serviceConfig.value();
        Method[] declaredMethods = value.getDeclaredMethods();

        HashMap<String, Method> pathMethodMap = (HashMap<String, Method>) caffeineCacheOperateService.getIfPresent("pathMethodMap");

        if (pathMethodMap == null) {
            pathMethodMap = new HashMap<>();
        }

        for (Method declaredMethod : declaredMethods) {
            //ServiceMapping serviceMapping = declaredMethod.getAnnotation(ServiceMapping.class);
            String path = originalPath;
            if (!pathMethodMap.containsKey(path) && path.equals(declaredMethod.getName().substring(declaredMethod.getName().lastIndexOf("_") + 1))) {
                //path和method的映射
                pathMethodMap.put(path, declaredMethod);
            }
        }
        caffeineCacheOperateService.put("pathMethodMap", pathMethodMap);
        /**
         * 分布式节点本地缓存一致性
         **/


        HashMap<String, Method> pathMethodMapFromCache = (HashMap<String, Method>) caffeineCacheOperateService.getIfPresent("pathMethodMap");
        Optional.ofNullable(pathMethodMapFromCache).ifPresent(map -> {
            try {
                /**
                 *todo 带参数
                 * */
                Object[] args = joinPoint.getArgs();
                int length = args.length;
                /**
                 * 带参数，todo 要解决不带@RequestBody和多参数问题
                 * */
                Method method = map.get(originalPath);
                if (length > 0) {
                    JsonContext jsonContext = buildJsonContext(joinPoint);
                    Object invokeResult = method.invoke(serviceIn, jsonContext);
                } else {
                    /**
                     * 不带参数
                     * */
                    Object invokeResult = method.invoke(serviceIn);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                throw CommonExceptionEnum.ILLEGALACCESSEXCEPTION.newException();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
                throw CommonExceptionEnum.INVOCATIONTARGETEXCEPTION.newException();
            }
        });
    }

    /**
     * 构建请求响应上下文
     */
    public JsonContext buildJsonContext(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        JSONObject reqJsonObject = (JSONObject) args[0];
        JSONObject rspJsonObject = new JSONObject();
        JsonContext jsonContext = (JsonContext) args[1];
        jsonContext.setReqJsonObject(reqJsonObject);
        jsonContext.setRspJsonObject(rspJsonObject);
        return jsonContext;
    }
}
